package com.xuecheng.manage_media.service;

import com.alibaba.fastjson.JSON;
import com.xuecheng.framework.domain.media.MediaFile;
import com.xuecheng.framework.domain.media.response.CheckChunkResult;
import com.xuecheng.framework.domain.media.response.MediaCode;
import com.xuecheng.framework.exception.ExceptionCast;
import com.xuecheng.framework.model.response.CommonCode;
import com.xuecheng.framework.model.response.ResponseResult;
import com.xuecheng.manage_media.config.RabbitmqConfig;
import com.xuecheng.manage_media.dao.MediaFileRepository;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import springfox.documentation.spring.web.json.Json;

import java.io.*;
import java.util.*;

@Service
public class MediaUploadService {
    @Autowired
    MediaFileRepository mediaFileRepository;
    @Value("${xc-service-manage-media.upload-location}")
    String uploadPath;
    //交换机
    @Autowired
    RabbitmqConfig rabbitmqConfig;
    //routingKey
    @Value("${xc-service-manage-media.mq.routingkey-media-video}")
    String routingkey_media_video;
    @Autowired
    RabbitTemplate rabbitTemplate;
            /**
          * 根据文件md5得到文件路径
          * 规则：
          * 一级目录：md5的第一个字符
          * 二级目录：md5的第二个字符
          * 三级目录：md5
          * 文件名：md5+文件扩展名
          * @param fileMd5 文件md5值
          * @param fileExt 文件扩展名
          * @return 文件路径
          */
    //文件上传注册,创建文件下载文件夹
    public ResponseResult register(String fileMd5, String fileName, Long fileSize, String mimetype, String fileExt) {
        //检查文件是否上传
        //1.得到文件的路径
        String filePath = getFilePath(fileMd5, fileExt);
        //创建文件
        File file = new File(filePath);
        //2.检查数据库文件是否存在
        Optional<MediaFile> optional = mediaFileRepository.findById(fileMd5);
        //文件存在直接返回
        if(file.exists()&&optional.isPresent()){
            return new ResponseResult(MediaCode.UPLOAD_FILE_REGISTER_EXIST);
        }
        //创建文件目录
        boolean fileFold = createFileFold(fileMd5);
        if(!fileFold){
            //上传文件目录创建失败
            return  new ResponseResult(MediaCode.UPLOAD_FILE_REGISTER_FAIL);
        }
        return new ResponseResult(CommonCode.SUCCESS);
    }
    //获取文件的路径
    private String getFilePath(String fileMd5,String fileExt){
        String filePath=uploadPath+fileMd5.substring(0,1)+"/"+fileMd5.substring(1,2)+"/"+fileMd5+"/"+fileMd5+"."+fileExt;
        return filePath;
    }
    //得到文件所在目录
    private String getFileFolderPath(String fileMd5){
        String fileFolderPath=uploadPath+fileMd5.substring(0,1)+"/"+fileMd5.substring(1,2)+"/"+fileMd5+"/"+fileMd5;
        return fileFolderPath;
    }
    //创建文件目录
    private  boolean createFileFold(String fileMd5){
        //获取文件坐在目录
        String fileFolderPath = getFileFolderPath(fileMd5);
        File file = new File(fileFolderPath);
        if(!file.exists()){
            //不存在，创建文件夹
            boolean mkdirs = file.mkdirs();
            return mkdirs;
        }
        return true;

    }
    //获得块文件路径
    private String getChunkFileFolderPath(String fileMd5){
        String fileChunkFolderPath=getFileFolderPath(fileMd5)+"/"+"chunks"+"/";
        return fileChunkFolderPath;
    }
    //获取文件相对路径,路径中去掉根目录
    private String getFileFolderRelativePath(String fileMd5,String fileExt){
       String  filePath=fileMd5.substring(0,1)+"/"+fileMd5.substring(1,2)+"/"+fileMd5+"/";
       return filePath;
    }
    //分块检查
    public CheckChunkResult checkchunk(String fileMd5, Integer chunk, Integer chunkSize) {
        //得到块文件所在路径
        String chunkFileFolderPath = getChunkFileFolderPath(fileMd5);
        //块文件的文件名称以1,2,3..序号命名，没有扩展名
        File chunkFile = new File(chunkFileFolderPath + chunk);
        if(chunkFile.exists()){
            return new CheckChunkResult(MediaCode.CHUNK_FILE_EXIST_CHECK,true);
        }else {
            return new CheckChunkResult(MediaCode.CHUNK_FILE_EXIST_CHECK,false);
        }
    }
    //上传分块
    public ResponseResult uploadchunk(MultipartFile file, Integer chunk, String fileMd5) {
        //判断块文件是否 为空
        if(file==null){
            ExceptionCast.cast(MediaCode.UPLOAD_FILE_REGISTER_ISNULL);
        }
        //创建块文件目录
        boolean chunkFileFolder = createChunkFileFolder(fileMd5);
        //块文件
        File chunkFile = new File(getChunkFileFolderPath(fileMd5) + chunk);
        InputStream inputStream=null;
        FileOutputStream fileOutputStream=null;
        try {
             inputStream= file.getInputStream();
             fileOutputStream=new FileOutputStream(chunkFile);
            IOUtils.copy(inputStream,fileOutputStream);
        } catch (Exception e) {
            e.printStackTrace();
            ExceptionCast.cast(MediaCode.CHUNK_FILE_UPLOAD_FAIL);
        }finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return new ResponseResult(CommonCode.SUCCESS);
    }
    //创建块文件目录
    private boolean createChunkFileFolder(String fileMd5){
        String chunkFileFolderPath = getChunkFileFolderPath(fileMd5);
        File file = new File(chunkFileFolderPath);
        if(!file.exists()){
            //穿件文件夹
            boolean mkdirs = file.mkdirs();
            return mkdirs;
        }
        return true;
    }
    //合并分块
    public ResponseResult mergechunks(String fileMd5, String fileName, Long fileSize, String mimetype, String fileExt) {
        //获取块文件路径
        String chunkFileFolderPath = getChunkFileFolderPath(fileMd5);
        File chunkfileFolder = new File(chunkFileFolderPath);
        if(!chunkfileFolder.exists()){
            chunkfileFolder.mkdirs();
        }
        //合并文件路径
        File mergeFile = new File(getFilePath(fileMd5, fileExt));
        //创建合并文件
        //判断合并文件是否存在，存在即删除
        if(mergeFile.exists()){
            mergeFile.delete();
        }
        boolean newFile=false;
        try {
            newFile = mergeFile.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
        if(!newFile){
            ExceptionCast.cast(MediaCode.MERGE_FILE_CREATEFAIL);
        }
        //获取已排序好的块列表
        List<File> chunkFiles = getChunkFile(chunkfileFolder);
        //合并文件
        mergeFile = mergeFile(mergeFile, chunkFiles);
        if(mergeFile==null){
            ExceptionCast.cast(MediaCode.CHUNK_FILE_UPLOAD_FAIL);
        }
        //校验文件
        boolean checkResult = checkFileMd5(mergeFile, fileMd5);
        if(!checkResult){
            ExceptionCast.cast(MediaCode.MERGE_FILE_CHECKFAIL);
        }
        //将文件保存到数据库中
        MediaFile mediaFile = new MediaFile();
        //设置文件id
        mediaFile.setFileId(fileMd5);
        //设置 文件名
        mediaFile.setFileName(fileMd5+"."+fileExt);
        mediaFile.setFileOriginalName(fileName);
        mediaFile.setFilePath(getFileFolderRelativePath(fileMd5,fileExt));
        mediaFile.setFileSize(fileSize);
        mediaFile.setUploadTime(new Date());
        mediaFile.setMimeType(mimetype);
        mediaFile.setFileType(fileExt);
        //状态为上传成功
        mediaFile.setFileStatus("301002");
        MediaFile file = mediaFileRepository.save(mediaFile);
        //获取fileId
        String fileId = file.getFileId();
        //向MQ发送消息
        return sendProcessVideoMsg(fileId);

    }
    //向Mqa发送消息
    public ResponseResult sendProcessVideoMsg(String fileId){
        Optional<MediaFile> optional = mediaFileRepository.findById(fileId);
        if(!optional.isPresent()){
            ExceptionCast.cast(CommonCode.FAIL);
        }
        MediaFile mediaFile = optional.get();
        Map<String,String> map=new HashMap<>();
        map.put("mediaId",fileId);
        String msg = JSON.toJSONString(map);
        try {
            rabbitTemplate.convertAndSend(rabbitmqConfig.EX_MEDIA_PROCESSTASK,routingkey_media_video,msg);
        } catch (AmqpException e) {
            e.printStackTrace();
        }
        return  new ResponseResult(CommonCode.SUCCESS);
    }
    //校验文件md5值
    private boolean checkFileMd5(File mergeFile,String fileMd5){
        if(mergeFile== null || StringUtils.isEmpty(fileMd5)) {
            return false;
        }
        //进行md5校验
        FileInputStream fileInputStream=null;
        try {
            fileInputStream = new FileInputStream(mergeFile);
            //得到文件的md5值
            String mergeFileMd5 = DigestUtils.md5Hex(fileInputStream);
            //比较md5
            if(fileMd5.equals(mergeFileMd5)){
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                fileInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return false;
    }

    //合并文件
    private File mergeFile(File mergeFile,List<File> chunkFiles){

        try {
            //创建写文件对象
            RandomAccessFile raf_write = new RandomAccessFile(mergeFile, "rw");
            //遍历分块文件进行合并
            //读取文件缓冲区
            byte[] bytes = new byte[1024];
            for (File chunkFile : chunkFiles) {
                //创建读文件
                RandomAccessFile raf_read = new RandomAccessFile(chunkFile, "r");
                int len=-1;
                while ((len=raf_read.read(bytes))!=-1){
                    raf_write.write(bytes,0,len);
                }
                raf_read.close();
            }
            raf_write.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return mergeFile;
    }
    //获取所有的块列表
    private List<File>  getChunkFile(File chunkfileFolder){
        //获取块文件的数组
        File[] chunkFiles = chunkfileFolder.listFiles();
        //转换为集合
        List<File> chunkFileList = Arrays.asList(chunkFiles);
        Collections.sort(chunkFileList, new Comparator<File>() {
            @Override
            public int compare(File o1, File o2) {
                if(Integer.parseInt(o1.getName())>Integer.parseInt(o2.getName())){
                    return 1;
                }
                return -1;
            }
        });
        return chunkFileList;

    }
}
